#include "aco.hpp"
#include "error.hpp"
#include "trace.hpp"
#include <iostream>
#include <fstream>
#include <climits>

#define ANTS_COUNT              10
#define RCP_RESULT_INVALID      1e9
#define EVAPORATION_COEFFICIENT 0.1

aco_c::~aco_c ( void )
{
        if ( NULL != ants )
        {
                for ( ants_v::iterator i = ants->begin (); i != ants->end (); i ++ )
                        delete ( ant_c * ) * i;
                delete ants;
        }

        if ( NULL != graph )
        {
                delete graph;
                graph = NULL;
        }

        if ( NULL != palette )
        {
                delete palette;
                palette = NULL;
        }
}

aco_c::aco_c ( end_condition_i & end_condition ) :
        graph           ( NULL ),
        palette         ( NULL ),
        ants            ( NULL ),
        best_ant        ( NULL ),
        r_modifier      ( 1.0 ),
        k_modifier      ( 1.0 ),
        k_bound         ( 0 ),
        r_bound         ( 0 ),
        end_condition   ( end_condition )
{
}

void aco_c::load_dimacs_graph ( const char * file_path )
{
        trace ( TRACE_LEVEL_NORMAL, "aco_c: loading graph...\n" );

        graph = graph_c::parse_dimacs ( file_path );
        throw_if ( NULL == graph, ERROR_INVALID_INPUT );
}

void aco_c::flush_dimacs_solution ( const char * file_path )
{
        trace ( TRACE_LEVEL_NORMAL, "aco_c: flushing solution...\n" );

        throw_if ( NULL == file_path, ERROR_INVALID_ARGUMENTS );
        throw_if ( NULL == best_ant, ERROR_STATE_UNEXPECTED );

        std::filebuf file_buffer;
        file_buffer.open ( file_path, std::ios::out );
        std::ostream stream ( & file_buffer );
        
        if ( best_ant->colouring_is_valid () )
        {
                stream << "s ROBUST_COL " << best_ant->get_colours_count () << std::endl;
                
                for ( long i = 0; i < graph->get_nodes_count (); i ++ )
                {
                        stream << "l " << i << " " << best_ant->get_route ()->at ( i ) << std::endl;
                }
        }
        else
        {
                stream << "ERROR: valid colouring was not found" << std::endl;
        }
        
        file_buffer.close ();
}

void aco_c::set_r_modifier ( double r_modifier )
{
        this->r_modifier = r_modifier;
}

void aco_c::set_k_modifier ( double k_modifier )
{
        this->k_modifier = k_modifier;
}

void aco_c::set_k_bound ( long k_bound )
{
        this->k_bound = k_bound;
}

void aco_c::set_r_bound ( long r_bound )
{
        this->r_bound = r_bound;
}

void aco_c::run ( void )
{
        throw_if ( NULL == graph, ERROR_STATE_UNEXPECTED );
        throw_if ( 0 == k_bound, ERROR_STATE_UNEXPECTED );

        trace ( TRACE_LEVEL_NORMAL, "aco_c: allocating resources...\n" );
        palette = new palette_c ( graph->get_nodes_count () /* - 1 */, k_bound );                
        ants = new ants_v ();       
        ants->reserve ( ANTS_COUNT );
        best_ant = new ant_c ();
        best_ant_result = RCP_RESULT_INVALID;

        for ( long i = 0; i < ANTS_COUNT; i ++ )
        {
                ant_c * ant = new ant_c ( graph, palette, k_bound );
                ant->restart_tour ();
                ants->push_back ( ant );
        }

        trace ( TRACE_LEVEL_NORMAL, "aco_c: running ants...\n" );

        end_condition.reset ();
        stopwatch.reset ();
        stopwatch.start ();

        do
        {
                for ( ants_v::iterator i = ants->begin (); i != ants->end (); i ++ )
                {
                        ant_c * ant = ( ant_c * ) * i;
                        
                        if ( ant->tour_completed () )
                        {
                                rcp_result ant_result = calculate_ant_result ( * ant );
                                
                                if ( ant_result < best_ant_result )
                                {
                                        //ant->trace ( TRACE_LEVEL_HIGH );        // TODO: switch to TRACE_LEVEL_LOW
                                        * best_ant = * ant;
                                        best_ant_result = ant_result;
                                }
                                else
                                {
                                        ant->restart_tour ();
                                }
                        }
                        else
                        {
                                ant->run_next_stage ();
                        }
                }

                update_pheromone_paths ();
        }
        while ( ! end_condition.examine ( * this ) );

        stopwatch.stop ();
        best_ant->trace ( TRACE_LEVEL_HIGH );
        
        double best_ant_penalty = 0;
        long n = graph->get_nodes_count ();
        for ( long i = 1; i < n; i ++ )
        {
                for ( long j = i + 1; j <= n; j ++)
                {
                        if ( best_ant->get_route ()->at ( i - 1 ) ==
                             best_ant->get_route ()->at ( j - 1 ) )
                        {
                                const edge_c * edge = graph->get_edge ( i , j );
                                if ( NULL != edge )
                                        best_ant_penalty += edge->get_probability ();
                        }
                        
                }
        }
        printf ( "Rigidity = %f\n", best_ant_penalty );
        printf ( "Efficiency = %f\n", 1.0 - ( double ) best_ant->get_tour_time () / r_bound );
        for ( ants_v::iterator i = ants->begin (); i != ants->end (); i ++ )
        {
                delete ( ant_c * ) * i;
        }
        
        delete ants;
        ants = NULL;
        
        delete palette;
        palette = NULL;
}

aco_c::rcp_result aco_c::calculate_ant_result ( const ant_c & ant ) const
{
        if ( ant.colouring_is_valid () )
        {
                //return ( rcp_result ) ( k_modifier * ant.get_colours_count () + r_modifier * ant.get_tour_time () );
                return ( aco_c::rcp_result ) ant.get_tour_time ();
        }
        else
                return RCP_RESULT_INVALID;
}

void aco_c::update_pheromone_paths ( void ) const
{
        for ( long i = 0; i < palette->get_levels_count (); i ++ )
        {
                for ( long j = 0; j < k_bound; j ++ )
                {
                        palette_c::paths_v * paths = palette->get_paths ( i, j );
                        for ( palette_c::paths_v::iterator path = paths->begin (); path != paths->end (); path ++ )
                        {
                                * path *= EVAPORATION_COEFFICIENT;
                        }
                }
        }
}

double aco_c::evaluate ( void ) const
{
        throw_if ( NULL == best_ant, ERROR_UNINITIALIZED );
        return ( double ) calculate_ant_result ( * best_ant );
}

long aco_c::get_running_time_ms ( void ) const
{
        return stopwatch.get_ms ();
}